package pl.edu.agh.iosr.gamblingzone.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;

import pl.edu.agh.iosr.gamblingzone.dao.GenericDAO;

/**
 * The Class GenericDAOBean.
 */
public class GenericDAOBean<T,ID extends Serializable> 
implements GenericDAO<T, ID>  {

	/** The page size. */
	private int pageSize = 10;

	/** The entity bean type. */
	private Class<T> entityBeanType;

	/** The session. */
	private Session session;

	/**
	 * Instantiates a new generic dao bean.
	 */
	@SuppressWarnings("unchecked")
	public GenericDAOBean() {
		this.entityBeanType = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#findAll()
	 */
	public List<T> findAll() {
		return findByCriteria(null);
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#findAll(int)
	 */
	public List<T> findAll(int page) {
		return findByCriteria(null, page);
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#findById(java.io.Serializable, boolean)
	 */
	@SuppressWarnings("unchecked")
	public T findById(ID id, boolean lock) {
		T entity;
		if (lock)
			entity = (T) session.load(getEntityBeanType(), id, LockMode.UPGRADE);
		else
			entity = (T) session.load(getEntityBeanType(), id);

		return entity;
	}
	
	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#getEntitiesCount()
	 */
	public int getEntitiesCount() {
		return getEntitiesCount(new ComplexCriterion());
	}

	/**
	 * Gets the entities count.
	 * 
	 * @param criterion the criterion
	 * 
	 * @return the entities count
	 */
	public int getEntitiesCount(ComplexCriterion criterion) {
		List<?> retList = findByCriteria(criterion.add(Projections.rowCount()));

		if (retList != null && retList.size() > 0) {
			Object resO = retList.get(0);
			if (resO instanceof Integer) {
				return ((Integer)resO).intValue();
			} else {
				return 0;
			}
		}

		return 0;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#getPageCount()
	 */
	public int getPageCount() {
		return getPageCount(new ComplexCriterion());
	}
	
	/**
	 * Gets the page count.
	 * 
	 * @param criterion the criterion
	 * 
	 * @return the page count
	 */
	public int getPageCount(ComplexCriterion criterion) {
		int userCount = getEntitiesCount(criterion);

		int retNum = userCount/pageSize;
		if (userCount%pageSize > 0) {
			retNum++;
		}

		return retNum;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#getPageSize()
	 */
	public int getPageSize() {
		return pageSize;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#makePersistent(java.lang.Object)
	 */
	public T makePersistent(T entity) {
		session.saveOrUpdate(entity);
		return entity;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#makeTransient(java.lang.Object)
	 */
	public void makeTransient(T entity) {
		session.delete(entity);

	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#setEntityManager(javax.persistence.EntityManager)
	 */
	public void setEntityManager(EntityManager entityManager) {
		session = (Session)entityManager.getDelegate();
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.iosr.gamblingzone.dao.GenericDAO#setPageSize(int)
	 */
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	/**
	 * Gets the entity bean type.
	 * 
	 * @return the entity bean type
	 */
	public Class<T> getEntityBeanType() {
		return entityBeanType;
	}

	/**
	 * Find by criteria.
	 * 
	 * @param criterion the criterion
	 * 
	 * @return the list< t>
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(ComplexCriterion criterion) {

		Criteria crit = createCriteria(criterion);

		return crit.list();
	}

	/**
	 * Find by criteria.
	 * 
	 * @param criterion the criterion
	 * @param page the page
	 * 
	 * @return the list< t>
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(ComplexCriterion criterion, int page) {
		Criteria crit = createCriteria(criterion);

		crit.setMaxResults(getPageSize());
		crit.setFirstResult(page*getPageSize());

		return crit.list();
	}

	/**
	 * Creates the criteria.
	 * 
	 * @param criterion the criterion
	 * 
	 * @return the criteria
	 */
	private Criteria createCriteria(ComplexCriterion criterion) {
		Criteria crit = session.createCriteria(getEntityBeanType());

		if (criterion != null) {
			for (Criterion c : criterion.getCriterions()) {
				crit.add(c);
			}

			for (Order o : criterion.getOrders()) {
				crit.addOrder(o);
			}

			if (criterion.getProjections().size() > 0) {
				ProjectionList projList = Projections.projectionList();
				for (Projection p: criterion.getProjections()) {
					projList.add(p);
				}
				crit.setProjection(projList);
			}	
		}

		return crit;
	}

	/**
	 * Gets the session.
	 * 
	 * @return the session
	 */
	public Session getSession() {
		return session;
	}

	/**
	 * Sets the session.
	 * 
	 * @param session the new session
	 */
	public void setSession(Session session) {
		this.session = session;
	}
}
